#include <riscv/visitor.h>

#define CHECK_REG(VISITOR, IR_REG) \
  Assert(VISITOR->ir_reg_to_reg.find(IR_REG) != VISITOR->ir_reg_to_reg.end(), \
      "can't find reg with ir reg:%s", IR_REG->get_name().c_str());

#define CHECK_FREG(VISITOR, IR_REG) \
  Assert(VISITOR->ir_reg_to_freg.find(IR_REG) != VISITOR->ir_reg_to_freg.end(), \
      "can't find freg with ir reg:%s", IR_REG->get_name().c_str());

#define CHECK_STACK(VISITOR, IR_REG) \
  Assert(VISITOR->ir_reg_to_stack.find(IR_REG) != VISITOR->ir_reg_to_stack.end(), \
      "can't find stack with ir reg:%s", IR_REG->get_name().c_str());

#define CHECK_BLOCK(VISITOR, IR_BLOCK) \
    Assert(VISITOR->ir_block_to_block.find(IR_BLOCK) != VISITOR->ir_block_to_block.end(), \
        "can't find block with ir block:%s", IR_BLOCK->get_name().c_str());

#define CHECK_GLOBAL(VISITOR, IR_GLOBAL_REG) \
  Assert(VISITOR->ir_global_reg_to_global.find(IR_GLOBAL_REG) != VISITOR->ir_global_reg_to_global.end(), \
      "can't find global with ir global reg:%s", IR_GLOBAL_REG->get_name().c_str());

#define CHECK_FUNC(VISITOR, IR_FUNC) \
  Assert(VISITOR->ir_func_to_func.find(IR_FUNC) != VISITOR->ir_func_to_func.end(), \
      "can't find function with ir function:%s", IR_FUNC->get_name().c_str());

namespace riscv {

  static void erase_comp_unit(CompUnit * comp_unit) {
    for(auto p : comp_unit->func_list) {
      delete p;
      p = nullptr;
    }
  }

  void Visitor::use_ir_reg(ir::Reg * ir_reg) {
    if(this->ir_reg_to_reg.find(ir_reg) != this->ir_reg_to_reg.end()) {
      ir_reg->sub_used_count();
      if(ir_reg->if_used() == false) {
        this->genarator->free_temp_reg(this->ir_reg_to_reg[ir_reg]);
      }
    } else if(this->ir_reg_to_freg.find(ir_reg) != this->ir_reg_to_freg.end()) {
      ir_reg->sub_used_count();
      if(ir_reg->if_used() == false) {
        this->genarator->free_temp_reg(this->ir_reg_to_freg[ir_reg]);
      }
    } else {
      Panic("cant find reg and freg with ir reg:%s", ir_reg->get_name().c_str());
    }
  }

  Reg Visitor::temp_trans_stored_reg(Reg temp_reg) {
    std::array<Reg, 8> temp_regs = {Reg::t0, Reg::t1, Reg::t2, Reg::t3, Reg::t4, Reg::t5, Reg::t6};
    std::array<Reg, 8> stored_regs = {Reg::s1, Reg::s2, Reg::s3, Reg::s4, Reg::s5, Reg::s6, Reg::s7};
    for(std::size_t i = 0; i < temp_regs.size(); ++i) {
      if(temp_regs.at(i) == temp_reg)
        return stored_regs.at(i);
    }
    Panic("temp_reg:%s is not temp reg", Regs::get_reg_name(temp_reg).c_str());
  }

  Visitor::Visitor(CompUnit * comp_unit) {
    Assert(comp_unit != nullptr, "comp_unit is null!");
    erase_comp_unit(comp_unit);
    this->comp_unit = comp_unit;
    this->genarator = std::make_unique<Genarator>(comp_unit);
    this->current_func = nullptr;
  }

  void Visitor::visit(ir::CompUnit & comp_unit) {
    for(ir::InstGlobalAlloc * inst : comp_unit.inst_global_alloc_list) {
      inst->accept(*this);
    }
    for(ir::Func * func : comp_unit.func_list) {
      func->accept(*this);
    }
    
    // generate begin and end block for every function
    for(ir::Func * func : comp_unit.func_list) {
      CHECK_FUNC(this, func);
      this->current_func = this->ir_func_to_func[func];
      // create block begin and end
      Block * block_begin = this->current_func->block_begin;
      Block * block_end = this->current_func->block_end;

      Assert(this->func_to_func_stack.find(this->current_func) != this->func_to_func_stack.end(),
          "can't find func_stack with func:%s", func->name.c_str());
      unsigned int stack_size = this->func_to_func_stack[this->current_func].all_size();
      stack_size = (stack_size / 16 + (stack_size % 16 ? 1 : 0)) * 16;
      // begin block
      this->genarator->gen_inst_addi(block_begin->inst_list, Reg::sp, Reg::sp, -stack_size);
      unsigned long index = 0;
      for(Reg reg : this->func_to_func_stack[this->current_func].get_stored_regs()) {
        this->genarator->gen_inst_sd(block_begin->inst_list, reg, index, Reg::sp);
        index += 8;
      }
      this->genarator->gen_inst_addi(block_begin->inst_list, Reg::s0, Reg::sp, stack_size);

      // args
      for(index = 0; index < func->arg_list.size(); ++index) {
        CHECK_STACK(this, func->arg_list.at(index));
        Stack stack = this->ir_reg_to_stack[func->arg_list.at(index)];
        if(func->arg_list.at(index)->get_type() == VarType::ptr_i32) {
          this->genarator->gen_inst_sw(block_begin->inst_list,
              Regs::get_arg_reg(index), stack.off, Reg::s0);
        } else if(func->arg_list.at(index)->get_type() == VarType::ptr_f32) {
          // convert double to float
          FReg arg_freg = Regs::get_arg_freg(index);
          this->genarator->gen_inst_fcvt_s_d(block_begin->inst_list,
              arg_freg, arg_freg);
          this->genarator->gen_inst_fsw(block_begin->inst_list,
              arg_freg, stack.off, Reg::s0);
        } else {
          Panic("invalid type:%s",
              enum_name(func->arg_list.at(index)->get_type()).c_str());
        }
      }

      // end block
      index = 0;
      for(Reg reg : this->func_to_func_stack[this->current_func].get_stored_regs()) {
        this->genarator->gen_inst_ld(block_end->inst_list, reg, index, Reg::sp);
        index += 8;
      }
      this->genarator->gen_inst_addi(block_end->inst_list, Reg::sp, Reg::sp, stack_size);
      this->genarator->gen_inst_ret(block_end->inst_list);
    }
  }

  void Visitor::visit(ir::Func & func) {
    // create funciton
    this->current_func = this->genarator->create_func(func.name);
    this->ir_func_to_func[&func] = this->current_func;
    // create block begin and end
    this->genarator->create_block_begin(*(this->current_func));
    this->genarator->create_block_end(*(this->current_func));

    // init func_stack
    this->func_to_func_stack[this->current_func].add_stored_reg(Reg::ra);
    this->func_to_func_stack[this->current_func].add_stored_reg(Reg::s0);
    // init ir_block_to_block
    for(ir::Block * ir_block : func.block_list) {
      Block * new_block = this->genarator->create_block(*(this->current_func));
      this->ir_block_to_block[ir_block] = new_block;
    }
    // alloc stack for function arguments
    for(unsigned long i = 0; i < func.arg_list.size(); ++i) {
      this->func_to_func_stack[this->current_func].add_alloc_size(4);
      Stack stack;
      stack.off = -this->func_to_func_stack[this->current_func].all_alloc_size();
      stack.size = 4;
      this->ir_reg_to_stack[func.arg_list.at(i)] = stack;
    }
    for(ir::Block * ir_block : func.block_list) {
      ir_block->accept(*this);
    }
  }

  void Visitor::visit(ir::Block & block) {
    this->current_block = this->ir_block_to_block[&block];
    for(ir::Inst * inst : block.inst_list) {
      inst->accept(*this);
    }
  }

  void Visitor::visit(ir::InstImm & inst_imm) {
    if(inst_imm.type == VarType::i32) {
      Reg temp_reg = this->genarator->alloc_temp_reg();
      this->ir_reg_to_reg[inst_imm.reg] = temp_reg;
      this->genarator->gen_inst_li(this->current_block->inst_list,
          temp_reg, inst_imm.int_number);
    } else if(inst_imm.type == VarType::f32) {
      FloatNumber * float_number = this->genarator->create_float_number(inst_imm.float_number);
      Reg temp_reg = this->genarator->alloc_temp_reg();
      this->genarator->gen_inst_lla(this->current_block->inst_list, temp_reg, float_number);
      FReg temp_freg = this->genarator->alloc_temp_freg();
      this->ir_reg_to_freg[inst_imm.reg] = temp_freg;
      this->genarator->gen_inst_flw(this->current_block->inst_list, temp_freg, 0, temp_reg);
      this->genarator->gen_inst_fcvt_d_s(this->current_block->inst_list, temp_freg, temp_freg);
      this->genarator->free_temp_reg(temp_reg);
    } else {
      Panic("imm type:%s invalid", enum_name(inst_imm.type).c_str());
    }
  }

  void Visitor::visit(ir::InstAlloc & inst_alloc) {
    this->func_to_func_stack[this->current_func].add_alloc_size(4);
    Stack stack;
    stack.off = -this->func_to_func_stack[this->current_func].all_alloc_size();
    stack.size = 4;
    this->ir_reg_to_stack[inst_alloc.reg] = stack;
  }

  void Visitor::visit(ir::InstStore & inst_store) {
    CHECK_STACK(this, inst_store.rd);
    Stack stack = this->ir_reg_to_stack[inst_store.rd];
    int off = stack.off;
    if(inst_store.src->get_type() == VarType::i32) {
      CHECK_REG(this, inst_store.src);
      Reg rd = this->ir_reg_to_reg[inst_store.src];
      use_ir_reg(inst_store.src);
      this->genarator->gen_inst_sw(this->current_block->inst_list,
          rd, off, Reg::s0);
    } else if(inst_store.src->get_type() == VarType::f32) {
      CHECK_FREG(this, inst_store.src);
      FReg rd = this->ir_reg_to_freg[inst_store.src];
      // double float to single
      this->genarator->gen_inst_fcvt_s_d(this->current_block->inst_list, rd, rd);
      use_ir_reg(inst_store.src);
      this->genarator->gen_inst_fsw(this->current_block->inst_list,
          rd, off, Reg::s0);
    } else {
      Panic("???");
    }
  }

  void Visitor::visit(ir::InstLoad & inst_load) {
    CHECK_STACK(this, inst_load.src);
    Stack stack = this->ir_reg_to_stack[inst_load.src];
    if(inst_load.rd->get_type() == VarType::i32) {
      Reg temp_reg = this->genarator->alloc_temp_reg();
      this->ir_reg_to_reg[inst_load.rd] = temp_reg;
      this->genarator->gen_inst_lw(this->current_block->inst_list,
          temp_reg, stack.off, Reg::s0);
      this->genarator->gen_inst_sext_w(this->current_block->inst_list,
          temp_reg, temp_reg);
    } else if(inst_load.rd->get_type() == VarType::f32) {
      FReg temp_freg = this->genarator->alloc_temp_freg();
      this->ir_reg_to_freg[inst_load.rd] = temp_freg;
      this->genarator->gen_inst_flw(this->current_block->inst_list,
          temp_freg, stack.off, Reg::s0);
      this->genarator->gen_inst_fcvt_d_s(this->current_block->inst_list,
          temp_freg, temp_freg);
    } else {
      Panic("type undfined");
    }
  }

  void Visitor::visit(ir::InstArrayAlloc & inst_array_alloc) {
    this->func_to_func_stack[this->current_func].add_alloc_size(4 * inst_array_alloc.count);
    Stack stack;
    stack.off = -this->func_to_func_stack[this->current_func].all_alloc_size();
    stack.size = 4 * inst_array_alloc.count;
    this->ir_reg_to_stack[inst_array_alloc.reg] = stack;
  }

  void Visitor::visit(ir::InstArrayStore & inst_array_store) {
    CHECK_STACK(this, inst_array_store.rd);
    Stack stack = this->ir_reg_to_stack[inst_array_store.rd];
    int off = stack.off;
    // get offset to temp_reg
    Reg temp_reg = this->genarator->alloc_temp_reg();
    this->genarator->gen_inst_addi(this->current_block->inst_list,
        temp_reg, Reg::s0, off);
    CHECK_REG(this, inst_array_store.index);
    Reg index_reg = this->ir_reg_to_reg[inst_array_store.index];
    use_ir_reg(inst_array_store.index);
    this->genarator->gen_inst_add(this->current_block->inst_list,
        temp_reg, temp_reg, index_reg);
    // store rd
    CHECK_REG(this, inst_array_store.src);
    Reg rd = this->ir_reg_to_reg[inst_array_store.src];
    use_ir_reg(inst_array_store.src);
    this->genarator->gen_inst_sw(this->current_block->inst_list,
        rd, 0, temp_reg);
    // costem free temp_reg
    this->genarator->free_temp_reg(temp_reg);
  }

  void Visitor::visit(ir::InstArrayLoad & inst_array_load) {
    CHECK_STACK(this, inst_array_load.src);
    Stack stack = this->ir_reg_to_stack[inst_array_load.src];
    int off = stack.off;
    // get offset to temp_reg
    Reg temp_reg = this->genarator->alloc_temp_reg();
    this->genarator->gen_inst_addi(this->current_block->inst_list,
        temp_reg, Reg::s0, off);
    CHECK_REG(this, inst_array_load.index);
    Reg index_reg = this->ir_reg_to_reg[inst_array_load.index];
    use_ir_reg(inst_array_load.index);
    this->genarator->gen_inst_add(this->current_block->inst_list,
        temp_reg, temp_reg, index_reg);
    // load src
    // costem free temp_reg
    this->genarator->free_temp_reg(temp_reg);
    Reg rd = this->genarator->alloc_temp_reg();
    this->ir_reg_to_reg[inst_array_load.rd] = rd;
    this->genarator->gen_inst_lw(this->current_block->inst_list,
        rd, 0, temp_reg);
    this->genarator->gen_inst_sext_w(this->current_block->inst_list,
        rd, rd);
  }

  void Visitor::visit(ir::InstGlobalAlloc & inst_global_alloc) {
    Global * global = this->genarator->create_global(inst_global_alloc.global_reg->get_name(),
        4, inst_global_alloc.int_number);
    this->ir_global_reg_to_global[inst_global_alloc.global_reg] = global;
  }

  void Visitor::visit(ir::InstGlobalStore & inst_global_store) {
    CHECK_GLOBAL(this, inst_global_store.global_rd);
    Global * global = this->ir_global_reg_to_global[inst_global_store.global_rd];
    Reg temp_reg = this->genarator->alloc_temp_reg();
    this->genarator->gen_inst_lla(this->current_block->inst_list, temp_reg, global);
    CHECK_REG(this, inst_global_store.src);
    Reg src = this->ir_reg_to_reg[inst_global_store.src];
    use_ir_reg(inst_global_store.src);
    this->genarator->gen_inst_sw(this->current_block->inst_list, src, 0, temp_reg);
    this->genarator->free_temp_reg(temp_reg);
  }

  void Visitor::visit(ir::InstGlobalLoad & inst_global_load) {
    CHECK_GLOBAL(this, inst_global_load.global_src);
    Global * global = this->ir_global_reg_to_global[inst_global_load.global_src];
    Reg temp_reg = this->genarator->alloc_temp_reg();
    this->genarator->gen_inst_lla(this->current_block->inst_list,
        temp_reg, global);
    this->genarator->gen_inst_lw(this->current_block->inst_list, temp_reg, 0, temp_reg);
    this->genarator->gen_inst_sext_w(this->current_block->inst_list,
        temp_reg, temp_reg);
    this->ir_reg_to_reg[inst_global_load.rd] = temp_reg;
  }

  void Visitor::visit(ir::InstI32ToF32 & inst_i32_to_f32) {
    FReg temp_freg = this->genarator->alloc_temp_freg();
    this->ir_reg_to_freg[inst_i32_to_f32.rd] = temp_freg;
    CHECK_REG(this, inst_i32_to_f32.src);
    Reg rs = this->ir_reg_to_reg[inst_i32_to_f32.src];
    use_ir_reg(inst_i32_to_f32.src);
    this->genarator->gen_inst_fcvt_d_l(this->current_block->inst_list,
        temp_freg, rs, FcvtRm::rtz);
  }

  void Visitor::visit(ir::InstF32ToI32 & inst_f32_to_i32) {
    Reg temp_reg = this->genarator->alloc_temp_reg();
    this->ir_reg_to_reg[inst_f32_to_i32.rd] = temp_reg;
    CHECK_FREG(this, inst_f32_to_i32.src);
    FReg rs = this->ir_reg_to_freg[inst_f32_to_i32.src];
    use_ir_reg(inst_f32_to_i32.src);
    this->genarator->gen_inst_fcvt_l_d(this->current_block->inst_list,
        temp_reg, rs, FcvtRm::rtz);
  }

  void Visitor::visit(ir::InstAdd & inst_add) {
    if(inst_add.rd->get_type() == VarType::i32) {
      CHECK_REG(this, inst_add.src1);
      Reg rs1 = this->ir_reg_to_reg[inst_add.src1];
      use_ir_reg(inst_add.src1);
      CHECK_REG(this, inst_add.src2);
      Reg rs2 = this->ir_reg_to_reg[inst_add.src2];
      use_ir_reg(inst_add.src2);
      Reg temp_reg = this->genarator->alloc_temp_reg();
      this->genarator->gen_inst_add(this->current_block->inst_list,
          temp_reg, rs1, rs2);
      this->ir_reg_to_reg[inst_add.rd] = temp_reg;
    } else if(inst_add.rd->get_type() == VarType::f32) {
      CHECK_FREG(this, inst_add.src1);
      FReg rs1 = this->ir_reg_to_freg[inst_add.src1];
      use_ir_reg(inst_add.src1);
      CHECK_FREG(this, inst_add.src2);
      FReg rs2 = this->ir_reg_to_freg[inst_add.src2];
      use_ir_reg(inst_add.src2);
      FReg temp_freg = this->genarator->alloc_temp_freg();
      this->genarator->gen_inst_fadd_d(this->current_block->inst_list,
          temp_freg, rs1, rs2);
      this->ir_reg_to_freg[inst_add.rd] = temp_freg;
    } else {
      Panic("undfined type");
    }
  }

  void Visitor::visit(ir::InstSub & inst_sub) {
    CHECK_REG(this, inst_sub.src1);
    Reg rs1 = this->ir_reg_to_reg[inst_sub.src1];
    use_ir_reg(inst_sub.src1);
    CHECK_REG(this, inst_sub.src2);
    Reg rs2 = this->ir_reg_to_reg[inst_sub.src2];
    use_ir_reg(inst_sub.src2);
    Reg temp_reg = this->genarator->alloc_temp_reg();
    this->genarator->gen_inst_sub(this->current_block->inst_list,
        temp_reg, rs1, rs2);
    this->ir_reg_to_reg[inst_sub.rd] = temp_reg;
  }

  void Visitor::visit(ir::InstMul & inst_mul) {
    CHECK_REG(this, inst_mul.src1);
    Reg rs1 = this->ir_reg_to_reg[inst_mul.src1];
    use_ir_reg(inst_mul.src1);
    CHECK_REG(this, inst_mul.src2);
    Reg rs2 = this->ir_reg_to_reg[inst_mul.src2];
    use_ir_reg(inst_mul.src2);
    Reg temp_reg = this->genarator->alloc_temp_reg();
    this->genarator->gen_inst_mul(this->current_block->inst_list,
        temp_reg, rs1, rs2);
    this->ir_reg_to_reg[inst_mul.rd] = temp_reg;
  }

  void Visitor::visit(ir::InstDiv & inst_div) {
    CHECK_REG(this, inst_div.src1);
    Reg rs1 = this->ir_reg_to_reg[inst_div.src1];
    use_ir_reg(inst_div.src1);
    CHECK_REG(this, inst_div.src2);
    Reg rs2 = this->ir_reg_to_reg[inst_div.src2];
    use_ir_reg(inst_div.src2);
    Reg temp_reg = this->genarator->alloc_temp_reg();
    this->genarator->gen_inst_div(this->current_block->inst_list,
        temp_reg, rs1, rs2);
    this->ir_reg_to_reg[inst_div.rd] = temp_reg;
  }

  void Visitor::visit(ir::InstJmp & inst_jmp) {
    CHECK_BLOCK(this, inst_jmp.block);
    Block * block = this->ir_block_to_block[inst_jmp.block];
    this->genarator->gen_inst_j(this->current_block->inst_list, block);
  }

  void Visitor::visit(ir::InstBne & inst_bne) {
    CHECK_REG(this, inst_bne.src1);
    Reg rs1 = this->ir_reg_to_reg[inst_bne.src1];
    use_ir_reg(inst_bne.src1);
    CHECK_REG(this, inst_bne.src2);
    Reg rs2 = this->ir_reg_to_reg[inst_bne.src2];
    use_ir_reg(inst_bne.src2);
    CHECK_BLOCK(this, inst_bne.block);
    Block * block = this->ir_block_to_block[inst_bne.block];
    this->genarator->gen_inst_bne(this->current_block->inst_list, rs1, rs2, block);
  }

  void Visitor::visit(ir::InstBeq & inst_beq) {
    CHECK_REG(this, inst_beq.src1);
    Reg rs1 = this->ir_reg_to_reg[inst_beq.src1];
    use_ir_reg(inst_beq.src1);
    CHECK_REG(this, inst_beq.src2);
    Reg rs2 = this->ir_reg_to_reg[inst_beq.src2];
    use_ir_reg(inst_beq.src2);
    CHECK_BLOCK(this, inst_beq.block);
    Block * block = this->ir_block_to_block[inst_beq.block];
    this->genarator->gen_inst_beq(this->current_block->inst_list, rs1, rs2, block);
  }

  void Visitor::visit(ir::InstCall & inst_call) {
    // move arguments to arguments reg
    for(unsigned long i = 0; i < inst_call.args.size(); ++i) {
      if(inst_call.args.at(i)->get_type() == VarType::i32) {
        CHECK_REG(this, inst_call.args.at(i));
        Reg reg = this->ir_reg_to_reg[inst_call.args.at(i)];
        use_ir_reg(inst_call.args.at(i));
        this->genarator->gen_inst_mv(this->current_block->inst_list,
            Regs::get_arg_reg(i), reg);
      } else if(inst_call.args.at(i)->get_type() == VarType::f32) {
        CHECK_FREG(this, inst_call.args.at(i));
        FReg freg = this->ir_reg_to_freg[inst_call.args.at(i)];
        use_ir_reg(inst_call.args.at(i));
        this->genarator->gen_inst_fmv_d(this->current_block->inst_list,
            Regs::get_arg_freg(i), freg);
      } else {
        Panic("invalid type:%s",
            enum_name(inst_call.args.at(i)->get_type()).c_str());
      }
    }
    CHECK_FUNC(this, inst_call.func);
    Func * func = this->ir_func_to_func[inst_call.func];
    // store temp reg
    for(Reg alloced_reg : this->genarator->get_alloced_reg()) {
      Reg stored_reg = temp_trans_stored_reg(alloced_reg);
      this->func_to_func_stack[func].add_stored_reg(stored_reg);
      this->genarator->gen_inst_mv(this->current_block->inst_list, stored_reg, alloced_reg);
    }
    this->genarator->gen_inst_call(this->current_block->inst_list, func);
    // recovery temp reg
    for(Reg alloced_reg : this->genarator->get_alloced_reg()) {
      Reg stored_reg = temp_trans_stored_reg(alloced_reg);
      this->genarator->gen_inst_mv(this->current_block->inst_list, alloced_reg, stored_reg);
    }
    // move result from reg a0 or fa0
    if(inst_call.rd->get_type() == VarType::i32) {
      Reg rd = this->genarator->alloc_temp_reg();
      this->ir_reg_to_reg[inst_call.rd] = rd;
      this->genarator->gen_inst_mv(this->current_block->inst_list, rd, Reg::a0);
    } else if(inst_call.rd->get_type() == VarType::f32) {
      FReg rd = this->genarator->alloc_temp_freg();
      this->ir_reg_to_freg[inst_call.rd] = rd;
      this->genarator->gen_inst_fmv_d(this->current_block->inst_list, rd, FReg::fa0);
    } else if(inst_call.rd->get_type() == VarType::none) {
      // nothing
    } else {
      Panic("invalid type:%s",
          enum_name(inst_call.rd->get_type()).c_str());
    }
  }

  void Visitor::visit(ir::InstRet & inst_ret) {
    if(inst_ret.type == VarType::i32) {
      CHECK_REG(this, inst_ret.reg);
      this->genarator->gen_inst_mv(this->current_block->inst_list,
          Reg::a0, this->ir_reg_to_reg[inst_ret.reg]);
      use_ir_reg(inst_ret.reg);
    } else if(inst_ret.type == VarType::f32) {
      CHECK_FREG(this, inst_ret.reg);
      this->genarator->gen_inst_fmv_d(this->current_block->inst_list,
          FReg::fa0, this->ir_reg_to_freg[inst_ret.reg]);
      use_ir_reg(inst_ret.reg);
    }
    this->genarator->gen_inst_j(this->current_block->inst_list, this->current_func->block_end);
    // end
  }
}
